import axios, {
  AxiosError,
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  InternalAxiosRequestConfig,
  AxiosHeaders,
  Method,
} from "axios";
import { showLoadingToast, closeToast } from "vant";
import { handleGeneralError, handleNetworkError } from "./tools";
// 定义接口
export interface HRequestInterceptors {
  // 请求拦截器（成功与失败）
  requestInterceptors?: (
    config: InternalAxiosRequestConfig
  ) => InternalAxiosRequestConfig;
  requestInterceptorsCatch?: (error: any) => any;
  // 相应拦截器（成功与失败）
  responseInterceptor?: (res: AxiosResponse) => AxiosResponse;
  responseInterceptorCatch?: (error: any) => any;
}

/**
 * @description
 * 继承接口: 定义每个请求的拦截器并且设置请求状态显示
 *  axios  config 需要传递的参数
 *  */
export interface HRequestConfig extends AxiosRequestConfig {
  interceptors?: HRequestInterceptors;
  // 是否展示请求加载状态
  showLoading?: boolean;
  needToken?: boolean;
  // headers?: BaseHeaders;
}
/**
 * @description 判断 axios 请求返回值是否属于 AxiosError
 */
function isErrorObject(error: any): error is AxiosError {
  return (error as AxiosError).isAxiosError !== undefined;
}
// 请求基础配置
const baseConfig: HRequestConfig = {
  baseURL: import.meta.env.PROD ? import.meta.env.VITE_APP_BASE_URL : "/",
  timeout: 10000,
};
// 请求加载显示状态
const DEFAULT_LOADING = false;

const DEFAULT_NEED_TOKEN = true;

const TOKEN = import.meta.env.VITE_TOKEN;

class Http {
  // 类型
  instance: AxiosInstance;
  interceptors?: HRequestInterceptors;
  showLoading: boolean;
  needToken: boolean;
  //   loading?: LoadingInstance;
  constructor(config: HRequestConfig) {
    this.instance = axios.create(config);
    this.interceptors = config.interceptors;
    this.showLoading = config.showLoading ?? DEFAULT_LOADING;
    this.needToken = config.needToken ?? DEFAULT_NEED_TOKEN;

    // 请求拦截器 类型
    this.instance.interceptors.request.use(
      this.interceptors?.requestInterceptors,
      this.interceptors?.requestInterceptorsCatch
    );

    // 响应拦截器 类型
    this.instance.interceptors.response.use(
      this.interceptors?.responseInterceptor,
      this.interceptors?.responseInterceptorCatch
    );

    // 所有实例的请求拦截
    this.instance.interceptors.request.use(
      (config) => {
        if (this.showLoading) {
          showLoadingToast({
            duration: 0,
            message: "加载中...",
            forbidClick: true,
          });
        }
        if (this.needToken) {
          config.headers[TOKEN] = localStorage.getItem(TOKEN) || "";
        }
        // 测试环境的地址替换
        if (import.meta.env.MODE === "test") {
          config.url = config.url?.replace(
            /\/recyclable\//g,
            "/recyclabledev/"
          );
        }
        console.log(config);

        return config;
      },
      (err: AxiosError) => {
        return err;
        // return Promise.reject(err);
      }
    );

    // 所有实例的响应拦截
    this.instance.interceptors.response.use(
      (res) => {
        closeToast();

        return res;
      },
      (err) => {
        return err;
      }
    );
  }

  server<T>(config: HRequestConfig): Promise<T> {
    this.showLoading = config.showLoading ?? DEFAULT_LOADING;
    this.needToken = config.needToken ?? DEFAULT_NEED_TOKEN;
    return new Promise((resolve, reject) => {
      this.instance
        .request(config)
        .then((res) => {
          console.log(
            isErrorObject(res),
            !handleGeneralError(
              res.data.data?.code || res.data.code,
              res.data.data?.msg || res.data.msg
            ),
            res
          );

          if (isErrorObject(res)) {
            const err = res as AxiosError;
            handleNetworkError(err.response?.status);
            reject(err);
          }
          console.log(res.data);

          if (!res) reject("请求失败");
          if (
            !handleGeneralError(
              res.data.data?.code || res.data.code,
              res.data.data?.msg || res.data.msg
            )
          ) {
            reject(res.data as T);
          }
          if (res.data.data?.code) {
            resolve(res.data.data as T);
          } else {
            resolve(res.data as T);
          }
        })
        .catch((err) => {
          reject(err);
        });
    });
  }
  // 对request 二次封装
  /**
   * @param T 返回值data 的类型声明
   * @param S 请求参数的类型声明
   * @param url  请求路径 必填
   * @param params 请求参数 可选
   * @param headers 请求头 可选
   * @param config  请求配置 可选
   * @returns  Promise 请求的响应参数
   */
  get<T, S>(
    url: string,
    params?: S,
    config?: HRequestConfig,
    headers?: AxiosHeaders
  ): Promise<AxiosResponse<T>> {
    return this.server<AxiosResponse<T>>({
      ...baseConfig,
      ...config,
      headers: { ...baseConfig.headers, ...config?.headers, ...headers },
      params,
      method: "GET",
      url,
    });
  }
  /**
   * @param T 返回值data 的类型声明
   * @param S 请求参数的类型声明
   * @param url  请求路径 必填
   * @param data 请求参数 可选
   * @param headers 请求头 可选
   * @param config  请求配置 可选
   * @returns  Promise 请求的响应参数
   */
  post<T, S>(
    url: string,
    data?: S,
    config?: HRequestConfig,
    headers?: AxiosHeaders
  ): Promise<AxiosResponse<T>> {
    return this.server<AxiosResponse<T>>({
      ...baseConfig,
      ...config,
      headers: { ...baseConfig.headers, ...config?.headers, ...headers },
      data,
      method: "POST",
      url,
    });
  }
  /**
   * @param T 返回值data 的类型声明
   * @param S 请求参数的类型声明
   * @param url  请求路径 必填
   * @param params 请求参数 可选
   * @param headers 请求头 可选
   * @param config  请求配置 可选
   * @returns  Promise 请求的响应参数
   */
  put<T, S>(
    url: string,
    data?: S,
    config?: HRequestConfig,
    headers?: AxiosHeaders
  ): Promise<AxiosResponse<T>> {
    return this.server<AxiosResponse<T>>({
      ...baseConfig,
      ...config,
      headers: { ...baseConfig.headers, ...config?.headers, ...headers },
      data,
      method: "PUT",
      url,
    });
  }
  /**
   * @param T 返回值data 的类型声明
   * @param S 请求参数的类型声明
   * @param url  请求路径 必填
   * @param params 请求参数 可选
   * @param headers 请求头 可选
   * @param config  请求配置 可选
   * @returns  Promise 请求的响应参数
   */
  delete<T, S>(
    url: string,
    data?: S,
    headers?: AxiosHeaders,
    config?: HRequestConfig
  ): Promise<AxiosResponse<T>> {
    return this.server<AxiosResponse<T>>({
      ...baseConfig,
      ...config,
      headers: { ...baseConfig.headers, ...config?.headers, ...headers },
      data,
      method: "DELETE",
      url,
    });
  }
  /**
   * @param T 返回值data 的类型声明
   * @param S 请求参数的类型声明
   * @param url  请求路径 必填
   * @param data 请求参数 可选
   * @param method 请求方法 可选 不传默认为 GET 当如果需要自定义 headers，config 时 必传
   * @param headers 请求头 可选
   * @param config  请求配置 可选
   * @returns  Promise 请求的响应参数
   * @example
   * request.request<{name:string,id:number},{username:string,password:string}>('/api/login', { username: 'admin', password: '123456' }, 'POST')
   */
  request<T, S>(
    url: string,
    data?: S,
    method?: Method,
    headers?: AxiosHeaders,
    config?: HRequestConfig
  ): Promise<AxiosResponse<T>> {
    return this.server<AxiosResponse<T>>({
      ...baseConfig,
      ...config,
      headers: { ...baseConfig.headers, ...config?.headers, ...headers },
      data,
      method: method ? method : "GET",
      url,
    });
  }
}

/**
 * @description
 * 使用 Http 类 new 一个 请求实例
 *
 *  @param {HRequestConfig} config
 *
 *  interceptors  当前实例的 自定义 请求 响应 拦截器 需要特殊处理的可以在这里配置
 */
const request = new Http({
  ...baseConfig,
  interceptors: {
    requestInterceptors: (config) => {
      return config;
    },
    requestInterceptorsCatch(err) {
      return err;
    },
    responseInterceptor(res) {
      return res;
    },
    responseInterceptorCatch(error) {
      return error;
    },
  },
});

export { Http, request };
